enable_hexdump = True

def recursion_lock(retval, lock_name = "____recursion_lock"):
    def decorator(func):
        def wrapper(self, *args, **kw):
            if getattr(self, lock_name, False):
                return retval
            setattr(self, lock_name, True)
            try:
                return func(self, *args, **kw)
            finally:
                setattr(self, lock_name, False)
        wrapper.__name__ = func.__name__
        return wrapper
    return decorator

class Container(object):
    def __init__(self, **kw):
        self.__dict__.update(kw)
    @recursion_lock("<...>")
    def __repr__(self):
        attrs = sorted("%s = %r" % (k, v) 
            for k, v in self.__dict__.iteritems() 
            if not k.startswith("_"))
        return "%s(%s)" % (self.__class__.__name__, ", ".join(attrs))
    def __str__(self):
        return self.__pretty_str__()
    @recursion_lock("<...>")
    def __pretty_str__(self, nesting = 1):
        attrs = []
        indentation = "    " * nesting
        for k, v in self.__dict__.iteritems():
            if not k.startswith("_"):
                text = [indentation, k, " = "]
                if hasattr(v, "__pretty_str__"):
                    text.append(v.__pretty_str__(nesting + 1))
                else:
                    text.append(repr(v))
                attrs.append("".join(text))
        attrs.sort()
        attrs.insert(0, self.__class__.__name__ + ":")
        return "\n".join(attrs)

class SequenceContainer(list):
    def __str__(self):
        return self.__pretty_str__()
    @recursion_lock("<...>")
    def __pretty_str__(self, nesting = 1):
        if not self:
            return "[]"
        indentation = "    " * nesting
        lines = ["["]
        for elem in self:
            lines.append("\n")
            lines.append(indentation)
            if hasattr(elem, "__pretty_str__"):
                lines.append(elem.__pretty_str__(nesting + 1))
            else:
                lines.append(repr(elem))
        lines.append("\n")
        lines.append("    " * (nesting - 1))
        lines.append("]")
        return "".join(lines)

class unread(object):
    def __repr__(self):
        return "<unread>"
unread = unread()

class OnDemandContainer(object):
    __slots__ = ["_obj", "_construct", "_stream", "_pos", "_context", "size"]
    def __init__(self, obj):
        self._obj = obj
    def __repr__(self):
        return "%s(%r)" % (self.__class__.__name__, self._obj)
    def __str__(self):
        return self.__pretty_str__(1)
    def __pretty_str__(self, nesting):
        if hasattr(self._obj, "__pretty_str__"):
            text = self._obj.__pretty_str__(nesting + 1)
        else:
            text = repr(self._obj)
        return "%s:\n%s%s" % (self.__class__.__name__, "    " * nesting, text)    
    
    @classmethod
    def create_lazy(cls, construct, stream, context):
        self = cls(unread)
        self._construct = construct
        self._stream = stream
        self._pos = stream.tell()
        self._context = context
        self.size = construct._getsize(context)
        return self
    
    def _get_value(self):
        if self._obj is not unread:
            return self._obj
        origpos = self._stream.tell()
        try:
            self._stream.seek(self._pos)
            self._obj = self._construct._parse(self._stream, self._context)
        finally:
            self._stream.seek(origpos)
        return self._obj
    def _set_value(self, value):
        self._obj = value
    value = property(_get_value, _set_value, 
        doc = "gets ('demands') or sets the value")
    
    has_value = property(lambda self: self._obj is not unread, 
        doc = "indicates whether the value exists (has been demanded)")

def int_to_bin(number, width = 32):
    if number < 0:
        number += 1 << width
    i = width - 1
    bits = ["\x00"] * width
    while number and i >= 0:
        bits[i] = "\x00\x01"[number & 1]
        number >>= 1
        i -= 1
    return "".join(bits)

def bin_to_int(bits, signed = False):
    number = 0
    bias = 0
    if signed and bits[0] == "\x01":
        bits = bits[1:]
        bias = 1 << len(bits)
    for b in bits:
        number <<= 1
        number |= ord(b)
    return number - bias

def swap_bytes(bits, byte_size = 8):
    i = 0
    l = len(bits)
    output = [""] * ((l // byte_size) + 1)
    j = len(output) - 1
    while i < l:
        output[j] = bits[i : i + byte_size]
        i += byte_size
        j -= 1
    return "".join(output)

_char_to_bin = {}
_bin_to_char = {}
for i in range(256):
    ch = chr(i)
    bin = int_to_bin(i, 8)
    _char_to_bin[ch] = bin
    _bin_to_char[bin] = ch    

def encode_bin(data):
    return "".join(_char_to_bin[ch] for ch in data)

def decode_bin(data):
    assert len(data) & 7 == 0, "data length must be a multiple of 8"
    i = 0
    j = 0
    l = len(data) // 8
    chars = [""] * l
    while j < l:
        chars[j] = _bin_to_char[data[i:i+8]]
        i += 8
        j += 1
    return "".join(chars)

_printable = dict((chr(i), ".") for i in range(256))
_printable.update((chr(i), chr(i)) for i in range(32, 128))

def hexdump(data, linesize = 16):
    prettylines = []
    if len(data) < 65536:
        fmt = "%%04X   %%-%ds   %%s"
    else:
        fmt = "%%08X   %%-%ds   %%s"
    fmt = fmt % (3 * linesize - 1,)
    for i in xrange(0, len(data), linesize):
        line = data[i : i + linesize]
        hextext = " ".join(b.encode("hex") for b in line)
        rawtext = "".join(_printable[b] for b in line)
        prettylines.append(fmt % (i, hextext, rawtext))
    return prettylines  











